/*
 * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */

 #include <stdio.h>
 #include "esp_log.h"
 #include "board_common.h"
 #include "led_strip.h"
 #include "iot_board.h"
 #if BOARD_IO_BASE_XL9535_2
 #include "xl9535.h"
 #endif
 
 #define BOARD_IO_LED(index) BOARD_IO_LED_##index
 #define BOARD_LED_TYPE(index) BOARD_LED_TYPE_##index
 #define BOARD_LED_POLARITY(index) BOARD_LED_POLARITY_##index
 #define BOARD_IO_BUTTON(index) BOARD_IO_BUTTON_##index
 #define BOARD_BUTTON_TYPE(index) BOARD_BUTTON_TYPE_##index
 #define BOARD_BUTTON_POLARITY(index) BOARD_BUTTON_POLARITY_##index
 
 /* default definitions */
 #ifndef BOARD_LED_NUM
 #if defined(BOARD_IO_LED_6)
 #define BOARD_LED_NUM 6
 #elif defined(BOARD_IO_LED_5)
 #define BOARD_LED_NUM 5
 #elif defined(BOARD_IO_LED_4)
 #define BOARD_LED_NUM 4
 #elif defined(BOARD_IO_LED_3)
 #define BOARD_LED_NUM 3
 #elif defined(BOARD_IO_LED_2)
 #define BOARD_LED_NUM 2
 #elif defined(BOARD_IO_LED_1)
 #define BOARD_LED_NUM 1
 #else
 #define BOARD_LED_NUM 0
 #endif
 #endif
 
 #ifndef BOARD_LED_POLARITY_1
 #define BOARD_LED_POLARITY_1 _POSITIVE
 #endif
 #ifndef BOARD_LED_POLARITY_2
 #define BOARD_LED_POLARITY_2 _POSITIVE
 #endif
 #ifndef BOARD_LED_POLARITY_3
 #define BOARD_LED_POLARITY_3 _POSITIVE
 #endif
 #ifndef BOARD_LED_POLARITY_4
 #define BOARD_LED_POLARITY_4 _POSITIVE
 #endif
 #ifndef BOARD_LED_POLARITY_5
 #define BOARD_LED_POLARITY_5 _POSITIVE
 #endif
 #ifndef BOARD_LED_POLARITY_6
 #define BOARD_LED_POLARITY_6 _POSITIVE
 #endif
 #ifndef BOARD_LED_POLARITY_7
 #define BOARD_LED_POLARITY_7 _POSITIVE
 #endif
 #ifndef BOARD_LED_POLARITY_8
 #define BOARD_LED_POLARITY_8 _POSITIVE
 #endif
 #ifndef BOARD_LED_POLARITY_9
 #define BOARD_LED_POLARITY_9 _POSITIVE
 #endif
 #ifndef BOARD_LED_POLARITY_10
 #define BOARD_LED_POLARITY_10 _POSITIVE
 #endif
 #ifndef BOARD_LED_POLARITY_11
 #define BOARD_LED_POLARITY_11 _POSITIVE
 #endif
 #ifndef BOARD_LED_POLARITY_12
 #define BOARD_LED_POLARITY_12 _POSITIVE
 #endif
 #ifndef BOARD_LED_POLARITY_13
 #define BOARD_LED_POLARITY_13 _POSITIVE
 #endif
 #ifndef BOARD_LED_POLARITY_14
 #define BOARD_LED_POLARITY_14 _POSITIVE
 #endif
 
 #ifndef BOARD_LED_TYPE_1
 #define BOARD_LED_TYPE_1 LED_TYPE_GPIO
 #endif
 #ifndef BOARD_LED_TYPE_2
 #define BOARD_LED_TYPE_2 LED_TYPE_GPIO
 #endif
 #ifndef BOARD_LED_TYPE_3
 #define BOARD_LED_TYPE_3 LED_TYPE_GPIO
 #endif
 #ifndef BOARD_LED_TYPE_4
 #define BOARD_LED_TYPE_4 LED_TYPE_GPIO
 #endif
 #ifndef BOARD_LED_TYPE_5
 #define BOARD_LED_TYPE_5 LED_TYPE_GPIO
 #endif
 #ifndef BOARD_LED_TYPE_6
 #define BOARD_LED_TYPE_6 LED_TYPE_GPIO
 #endif
 #ifndef BOARD_LED_TYPE_7
 #define BOARD_LED_TYPE_7 LED_TYPE_GPIO
 #endif
 #ifndef BOARD_LED_TYPE_8
 #define BOARD_LED_TYPE_8 LED_TYPE_GPIO
 #endif
 #ifndef BOARD_LED_TYPE_9
 #define BOARD_LED_TYPE_9 LED_TYPE_GPIO
 #endif
 #ifndef BOARD_LED_TYPE_10
 #define BOARD_LED_TYPE_10 LED_TYPE_GPIO
 #endif
 #ifndef BOARD_LED_TYPE_11
 #define BOARD_LED_TYPE_11 LED_TYPE_GPIO
 #endif
 #ifndef BOARD_LED_TYPE_12
 #define BOARD_LED_TYPE_12 LED_TYPE_GPIO
 #endif
 #ifndef BOARD_LED_TYPE_13
 #define BOARD_LED_TYPE_13 LED_TYPE_GPIO
 #endif
 #ifndef BOARD_LED_TYPE_14
 #define BOARD_LED_TYPE_14 LED_TYPE_GPIO
 #endif
 
 #ifndef BOARD_BUTTON_NUM
 #if defined(BOARD_IO_BUTTON_6)
 #define BOARD_BUTTON_NUM 6
 #elif defined(BOARD_IO_BUTTON_5)
 #define BOARD_BUTTON_NUM 5
 #elif defined(BOARD_IO_BUTTON_4)
 #define BOARD_BUTTON_NUM 4
 #elif defined(BOARD_IO_BUTTON_3)
 #define BOARD_BUTTON_NUM 3
 #elif defined(BOARD_IO_BUTTON_2)
 #define BOARD_BUTTON_NUM 2
 #elif defined(BOARD_IO_BUTTON_1)
 #define BOARD_BUTTON_NUM 1
 #else
 #define BOARD_BUTTON_NUM 0
 #endif
 #endif
 
 #ifndef BOARD_BUTTON_POLARITY_1
 #define BOARD_BUTTON_POLARITY_1 _NEGATIVE
 #endif
 #ifndef BOARD_BUTTON_POLARITY_2
 #define BOARD_BUTTON_POLARITY_2 _NEGATIVE
 #endif
 #ifndef BOARD_BUTTON_POLARITY_3
 #define BOARD_BUTTON_POLARITY_3 _NEGATIVE
 #endif
 #ifndef BOARD_BUTTON_POLARITY_4
 #define BOARD_BUTTON_POLARITY_4 _NEGATIVE
 #endif
 #ifndef BOARD_BUTTON_POLARITY_5
 #define BOARD_BUTTON_POLARITY_5 _NEGATIVE
 #endif
 #ifndef BOARD_BUTTON_POLARITY_6
 #define BOARD_BUTTON_POLARITY_6 _NEGATIVE
 #endif
 #ifndef BOARD_BUTTON_POLARITY_7
 #define BOARD_BUTTON_POLARITY_7 _NEGATIVE
 #endif
 #ifndef BOARD_BUTTON_POLARITY_8
 #define BOARD_BUTTON_POLARITY_8 _NEGATIVE
 #endif
 #ifndef BOARD_BUTTON_POLARITY_9
 #define BOARD_BUTTON_POLARITY_9 _NEGATIVE
 #endif
 #ifndef BOARD_BUTTON_POLARITY_10
 #define BOARD_BUTTON_POLARITY_10 _NEGATIVE
 #endif
 #ifndef BOARD_BUTTON_POLARITY_11
 #define BOARD_BUTTON_POLARITY_11 _NEGATIVE
 #endif
 #ifndef BOARD_BUTTON_POLARITY_12
 #define BOARD_BUTTON_POLARITY_12 _NEGATIVE
 #endif
 #ifndef BOARD_BUTTON_POLARITY_13
 #define BOARD_BUTTON_POLARITY_13 _NEGATIVE
 #endif
 #ifndef BOARD_BUTTON_POLARITY_14
 #define BOARD_BUTTON_POLARITY_14 _NEGATIVE
 #endif
 
 
 #ifndef BOARD_NAME
 #define BOARD_NAME "UNDEFINED"
 #endif
 #ifndef BOARD_VENDOR
 #define BOARD_VENDOR "UNDEFINED"
 #endif
 #ifndef BOARD_URL
 #define BOARD_URL "UNDEFINED"
 #endif
 #define BOARD_INFO ("BOARD_NAME: " BOARD_NAME "\n" "BOARD_VENDOR: " BOARD_VENDOR "\n" "BOARD_URL: " BOARD_URL "\n")
 
 #define BOARD_LED_CONFIG(index) \
     { .io_num = BOARD_IO_LED(index),\
       .type = BOARD_LED_TYPE(index),\
       .state = 0,\
       .polarity = BOARD_LED_POLARITY(index) }
 
 #define BOARD_BUTTON_CONFIG(index) \
     { .io_num = BOARD_IO_BUTTON(index), \
       .polarity = BOARD_BUTTON_POLARITY(index) }
 
 typedef struct {
     uint8_t io_num;
     uint8_t type;
     uint8_t state;
     union {
         uint8_t polarity;
         #ifdef LED_TYPE_RGB
         led_strip_t *p_strip;
         #endif
     };
 } board_led_t;
 
 typedef struct {
     uint8_t io_num;
     uint8_t polarity;
     button_handle_t handle;
 } board_button_t;
 
 #if defined(CONFIG_BOARD_LED_INIT) && (BOARD_LED_NUM > 0)
 static board_led_t s_led[] = {
     BOARD_LED_CONFIG(1),
 #if BOARD_LED_NUM > 1
     BOARD_LED_CONFIG(2),
 #endif
 #if BOARD_LED_NUM > 2
     BOARD_LED_CONFIG(3),
 #endif
 #if BOARD_LED_NUM > 3
     BOARD_LED_CONFIG(4),
 #endif
 #if BOARD_LED_NUM > 4
     BOARD_LED_CONFIG(5),
 #endif
 #if BOARD_LED_NUM > 5
     BOARD_LED_CONFIG(6),
 #endif
 #if BOARD_LED_NUM > 6
     BOARD_LED_CONFIG(7),
 #endif
 #if BOARD_LED_NUM > 7
     BOARD_LED_CONFIG(8),
 #endif
 #if BOARD_LED_NUM > 8
     BOARD_LED_CONFIG(9),
 #endif
 #if BOARD_LED_NUM > 9
     BOARD_LED_CONFIG(10),
 #endif
 #if BOARD_LED_NUM > 10
     BOARD_LED_CONFIG(11),
 #endif
 
 #if BOARD_LED_NUM > 11
     BOARD_LED_CONFIG(12),
 #endif
 #if BOARD_LED_NUM > 12
     BOARD_LED_CONFIG(13),
 #endif
 #if BOARD_LED_NUM > 13
     BOARD_LED_CONFIG(14),
 #endif
 #if BOARD_LED_NUM > 14
     BOARD_LED_CONFIG(15),
 #endif
 #if BOARD_LED_NUM > 14
 #error "Please increase length if required"
 #endif
 };
 #endif
 
 #if defined(CONFIG_BOARD_BUTTON_INIT) && (BOARD_BUTTON_NUM > 0)
 static board_button_t s_button[] = {
     BOARD_BUTTON_CONFIG(1),
 #if BOARD_BUTTON_NUM > 1
     BOARD_BUTTON_CONFIG(2),
 #endif
 #if BOARD_BUTTON_NUM > 2
     BOARD_BUTTON_CONFIG(3),
 #endif
 #if BOARD_BUTTON_NUM > 3
     BOARD_BUTTON_CONFIG(4),
 #endif
 #if BOARD_BUTTON_NUM > 4
     BOARD_BUTTON_CONFIG(5),
 #endif
 #if BOARD_BUTTON_NUM > 5
     BOARD_BUTTON_CONFIG(6),
 #endif
 #if BOARD_BUTTON_NUM > 6
     BOARD_BUTTON_CONFIG(7),
 #endif
 #if BOARD_BUTTON_NUM > 7
     BOARD_BUTTON_CONFIG(8),
 #endif
 #if BOARD_BUTTON_NUM > 8
     BOARD_BUTTON_CONFIG(9),
 #endif
 #if BOARD_BUTTON_NUM > 9
     BOARD_BUTTON_CONFIG(10),
 #endif
 #if BOARD_BUTTON_NUM > 10
     BOARD_BUTTON_CONFIG(11),
 #endif
 #if BOARD_BUTTON_NUM > 11
     BOARD_BUTTON_CONFIG(12),
 #endif
 #if BOARD_BUTTON_NUM > 12
     BOARD_BUTTON_CONFIG(13),
     #endif
 #if BOARD_BUTTON_NUM > 13
 #error "Please increase length if required"
 #endif
 };
 #endif
 
 /**** Private handle ****/
 static const char *TAG = "Board_Common";
 static i2c_bus_handle_t s_i2c0_bus_handle = NULL;
 static spi_bus_handle_t s_spi2_bus_handle = NULL;
 static bool s_board_is_init = false;
 static bool s_board_led_is_init = false;
 static bool s_board_button_is_init = false;
 static bool s_board_gpio_is_init = false;
 
 /* board specific callbacks */
 static board_specific_callbacks_t s_board_callbacks = {
     .pre_init_cb = NULL,
     .pre_deinit_cb = NULL,
     .post_init_cb = NULL,
     .post_deinit_cb = NULL,
     .get_handle_cb = NULL,
 };
 
 static esp_err_t board_gpio_init(void)
 {
     BOARD_CHECK(!s_board_gpio_is_init, "gpio not inited", ESP_ERR_INVALID_STATE);
     esp_err_t ret = ESP_OK;
 #ifdef CONFIG_BOARD_GPIO_INIT
     gpio_config_t io_conf = {
         //disable interrupt
         .intr_type = GPIO_INTR_DISABLE,
         //disable pull-down mode
         .pull_down_en = 0,
         //disable pull-up mode
         .pull_up_en = 0,
     };
 
 #ifdef BOARD_IO_PIN_SEL_OUTPUT
     //configure GPIO with the given settings
     io_conf.mode = GPIO_MODE_OUTPUT;
     io_conf.pin_bit_mask = BOARD_IO_PIN_SEL_OUTPUT;
     ret = gpio_config(&io_conf);
     BOARD_CHECK(ret == ESP_OK, "gpio out config failed", ret);
     ESP_LOGI(TAG, "gpio out init succeed");
     s_board_gpio_is_init = true;
 #endif
 
 #ifdef BOARD_IO_PIN_SEL_INPUT
     io_conf.mode = GPIO_MODE_INPUT;
     io_conf.pin_bit_mask = BOARD_IO_PIN_SEL_INPUT;
     ret = gpio_config(&io_conf);
     BOARD_CHECK(ret == ESP_OK, "gpio in config failed", ret);
     ESP_LOGI(TAG, "gpio in init succeed");
     s_board_gpio_is_init = true;
 #endif
 
 #if !(BOARD_IO_PIN_SEL_INPUT || BOARD_IO_PIN_SEL_OUTPUT)
     ESP_LOGW(TAG, "no gpio defined");
     (void) io_conf;
     s_board_gpio_is_init = false;
 #endif
 
 #endif
     return ret;
 }
 
 static esp_err_t board_gpio_deinit(void)
 {
     BOARD_CHECK(s_board_gpio_is_init, "gpio not inited", ESP_ERR_INVALID_STATE);
 #ifdef CONFIG_BOARD_GPIO_INIT
     s_board_gpio_is_init = false;
     ESP_LOGI(TAG, "gpio deinit succeed");
 #endif
     return ESP_OK;
 }
 
 static esp_err_t board_i2c_bus_init(void)
 {
 #if defined(CONFIG_BOARD_I2C0_INIT) && defined(BOARD_IO_I2C0_SDA) && defined(BOARD_IO_I2C0_SCL)
     i2c_config_t board_i2c_conf = {
         .mode = I2C_MODE_MASTER,
         .sda_io_num = BOARD_IO_I2C0_SDA,
         .sda_pullup_en = CONFIG_BOARD_I2C0_SDA_PULLUP_EN,
         .scl_io_num = BOARD_IO_I2C0_SCL,
         .scl_pullup_en = CONFIG_BOARD_I2C0_SCL_PULLUP_EN,
         .master.clk_speed = CONFIG_BOARD_I2C0_SPEED,
     };
     i2c_bus_handle_t handle = i2c_bus_create(I2C_NUM_0, &board_i2c_conf);
     BOARD_CHECK(handle != NULL, "i2c_bus create failed", ESP_FAIL);
     s_i2c0_bus_handle = handle;
     ESP_LOGI(TAG, "i2c bus create succeed");
 #elif defined(CONFIG_BOARD_I2C0_INIT)
     ESP_LOGW(TAG, "i2c0 Init, but no i2c0 io defined");
 #endif
     return ESP_OK;
 }
 
 static esp_err_t board_i2c_bus_deinit(void)
 {
     if (s_i2c0_bus_handle != NULL) {
         i2c_bus_delete(&s_i2c0_bus_handle);
         BOARD_CHECK(s_i2c0_bus_handle == NULL, "i2c_bus delete failed", ESP_FAIL);
         ESP_LOGI(TAG, "i2c bus delete succeed");
     }
     return ESP_OK;
 }
 
 static esp_err_t board_spi_bus_init(void)
 {
 #if defined(CONFIG_BOARD_SPI2_INIT) && defined(BOARD_IO_SPI2_MISO) && defined(BOARD_IO_SPI2_MOSI) && defined(BOARD_IO_SPI2_SCK)
     spi_config_t bus_conf = {
         .miso_io_num = BOARD_IO_SPI2_MISO,
         .mosi_io_num = BOARD_IO_SPI2_MOSI,
         .sclk_io_num = BOARD_IO_SPI2_SCK,
         .max_transfer_sz = CONFIG_BOARD_SPI2_MAX_TXF_SIZE
     };
     s_spi2_bus_handle = spi_bus_create(SPI2_HOST, &bus_conf);
     BOARD_CHECK(s_spi2_bus_handle != NULL, "spi_bus2 create failed", ESP_FAIL);
     ESP_LOGI(TAG, "spi2 bus create succeed");
 #elif defined(CONFIG_BOARD_SPI2_INIT)
     ESP_LOGW(TAG, "spi2 Init, but no spi2 io defined");
 #endif
     return ESP_OK;
 }
 
 static esp_err_t board_spi_bus_deinit(void)
 {
     // if (s_spi2_bus_handle != NULL) {
     //     spi_bus_delete(&s_spi2_bus_handle);
     //     BOARD_CHECK(s_spi2_bus_handle == NULL, "spi2 bus delete failed", ESP_FAIL);
     //     ESP_LOGI(TAG, "spi2 bus delete succeed");
     // }
     return ESP_OK;
 }
 
 static esp_err_t board_led_init(void)
 {
 #if defined(CONFIG_BOARD_LED_INIT) && (BOARD_LED_NUM > 0)
     ESP_LOGI(TAG, "BOARD_LED_NUM = %d", BOARD_LED_NUM);
     gpio_config_t io_conf = {
         //disable interrupt
         .intr_type = GPIO_INTR_DISABLE,
         //disable pull-down mode
         .pull_down_en = 0,
         //disable pull-up mode
         .pull_up_en = 0,
         .mode = GPIO_MODE_OUTPUT,
     };
 
     for (size_t i = 0; i < BOARD_LED_NUM; i++) {
         if (s_led[i].type == LED_TYPE_GPIO) {
             io_conf.pin_bit_mask = 1ULL << s_led[i].io_num;
             esp_err_t ret = gpio_config(&io_conf);
             BOARD_CHECK(ret == ESP_OK, "led config failed", ret);
             ESP_LOGI(TAG, "led init succeed io=%d", i);
         } 
 #if BOARD_IO_BASE_XL9535_2
         else if(s_led[i].type == LED_TYPE_I2C_EXIO) {
             // 第一路IO扩展芯片
             if(s_led[i].io_num >= BOARD_IO_BASE_XL9535 && s_led[i].io_num < BOARD_IO_BASE_XL9535_2){
                 xl9535_pin_mode(i2c_dev_xl9535[0], s_led[i].io_num-BOARD_IO_BASE_XL9535, XL9535_GPIO_FLAG_OUTPUT);
             }
             // 第二路IO扩展芯片
             else {
                 xl9535_pin_mode(i2c_dev_xl9535[1], s_led[i].io_num-BOARD_IO_BASE_XL9535_2, XL9535_GPIO_FLAG_OUTPUT);
             }
         }
 #endif
 #ifdef LED_TYPE_RGB
         else {
             /* LED strip initialization with the GPIO and pixels number*/
             static int rmt_channel = 0;
             BOARD_CHECK(rmt_channel < SOC_RMT_CHANNELS_PER_GROUP, "led no free rmt channel", ESP_FAIL);
             s_led[i].p_strip = led_strip_init(rmt_channel, s_led[i].io_num, 1);
             BOARD_CHECK(s_led[i].p_strip != NULL, "led(RGB) config failed", ESP_FAIL);
             ++rmt_channel;
             /* Set all LED off to clear all pixels */
             s_led[i].p_strip->clear(s_led[i].p_strip, 50);
             ESP_LOGI(TAG, "led(RGB) init succeed io=%d", s_led[i].io_num);
         }
 #endif
     }
 
     s_board_led_is_init = true;
 #elif defined(CONFIG_BOARD_LED_INIT)
     ESP_LOGW(TAG, "LED Init, but no LED defined");
 #endif
     return ESP_OK;
 }
 
 static esp_err_t board_led_deinit(void)
 {
 #if defined(CONFIG_BOARD_LED_INIT) && (BOARD_LED_NUM > 0)
     BOARD_CHECK(s_board_led_is_init, "led not inited", ESP_ERR_INVALID_STATE);
     for (size_t i = 0; i < BOARD_LED_NUM; i++) {
 #ifdef LED_TYPE_RGB
         if (s_led[i].type == LED_TYPE_RGB) {
             esp_err_t ret = led_strip_denit(s_led[i].p_strip);
             BOARD_CHECK(ret == ESP_OK, "led(RGB) deinit failed", ret);
         }
 #endif
     }
     s_board_led_is_init = false;
     ESP_LOGI(TAG, "led deinit succeed");
 #endif
     return ESP_OK;
 }
 
 static button_handle_t board_button_io_to_handle(int gpio_num)
 {
 #if defined(CONFIG_BOARD_BUTTON_INIT) && (BOARD_BUTTON_NUM > 0)
     size_t i = 0;
     bool if_find = false;
     for (i = 0; i < BOARD_BUTTON_NUM; i++) {
         if (gpio_num == s_button[i].io_num) {
             if_find = true;
             break;
         }
     }
 
     if (!if_find) {
         ESP_LOGW(TAG, "Button not found io=%d", gpio_num);
         return NULL;
     }
 
     return s_button[i].handle;
 #endif
     return NULL;
 }
 
  int board_button_handle_to_io(void *handle)
 {
 #if defined(CONFIG_BOARD_BUTTON_INIT) && (BOARD_BUTTON_NUM > 0)
     size_t i = 0;
     bool if_find = false;
     for (i = 0; i < BOARD_BUTTON_NUM; i++) {
         if (handle == s_button[i].handle) {
             if_find = true;
             break;
         }
     }
 
     if (!if_find) {
         ESP_LOGW(TAG, "Button not found handle=%p", handle);
         return -1;
     }
 
     return s_button[i].io_num;
 #endif
     return -1;
 }
 
 #if BOARD_CUSTOM_BUTTON_EN
 static esp_err_t custom_button_gpio_init(void *param)
 {
     button_gpio_config_t *cfg = (button_gpio_config_t *)param;
     // 第一路IO扩展芯片
     if(cfg->gpio_num >= BOARD_IO_BASE_XL9535 && cfg->gpio_num < BOARD_IO_BASE_XL9535_2){
         xl9535_pin_mode(i2c_dev_xl9535[0], cfg->gpio_num-BOARD_IO_BASE_XL9535, XL9535_GPIO_FLAG_INPUT);
     }
     // 第二路IO扩展芯片
     else {
         xl9535_pin_mode(i2c_dev_xl9535[1], cfg->gpio_num-BOARD_IO_BASE_XL9535_2, XL9535_GPIO_FLAG_INPUT);
     }
     return ESP_OK;
 }
 
 static uint8_t custom_button_gpio_get_key_value(void *param)
 {
     button_gpio_config_t *cfg = (button_gpio_config_t *)param;
     // 第一路IO扩展芯片
     if(cfg->gpio_num >= BOARD_IO_BASE_XL9535 && cfg->gpio_num < BOARD_IO_BASE_XL9535_2){
         return xl9535_digital_read(i2c_dev_xl9535[0], cfg->gpio_num-BOARD_IO_BASE_XL9535);
     }
     // 第二路IO扩展芯片
     else {
         return xl9535_digital_read(i2c_dev_xl9535[1], cfg->gpio_num-BOARD_IO_BASE_XL9535_2);
     }
     // return xl9535_digital_read(i2c_dev_xl9535, cfg->gpio_num);
 }
 
 static esp_err_t custom_button_gpio_deinit(void *param)
 {
     button_gpio_config_t *cfg = (button_gpio_config_t *)param;
 
     return ESP_OK;
 }
 #endif
 
 static esp_err_t board_button_init()
 {
 #if defined(CONFIG_BOARD_BUTTON_INIT) && (BOARD_BUTTON_NUM > 0)
 
 #if BOARD_CUSTOM_BUTTON_EN
     button_config_t cfg = {
         .type = BUTTON_TYPE_CUSTOM,
         .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
         .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
         .custom_button_config = {
             .button_custom_init = custom_button_gpio_init,
             .button_custom_deinit = custom_button_gpio_deinit,
             .button_custom_get_key_value = custom_button_gpio_get_key_value,
             .active_level = 0,
             .priv = NULL,
         },
     };
     for (size_t i = 0; i < BOARD_BUTTON_NUM; i++) {
         button_gpio_config_t *gpio_cfg = calloc(1, sizeof(button_gpio_config_t));
         gpio_cfg->active_level = s_button[i].polarity;
         gpio_cfg->gpio_num = s_button[i].io_num;
         cfg.custom_button_config.priv = gpio_cfg;
         s_button[i].handle = iot_button_create(&cfg);
         BOARD_CHECK(s_button[i].handle != NULL, "button ok create failed", ESP_FAIL);
         ESP_LOGI(TAG, "button init succeed, io=%d plr=%d",  s_button[i].io_num, s_button[i].polarity);
     }
 #else
     button_config_t cfg = {
         .type = BUTTON_TYPE_GPIO,
         .gpio_button_config = {
             .gpio_num = 0,
             .active_level = 0,
         },
     };
     for (size_t i = 0; i < BOARD_BUTTON_NUM; i++) {
         cfg.gpio_button_config.gpio_num = s_button[i].io_num;
         cfg.gpio_button_config.active_level = s_button[i].polarity;
         s_button[i].handle = iot_button_create(&cfg);
         BOARD_CHECK(s_button[i].handle != NULL, "button ok create failed", ESP_FAIL);
         ESP_LOGI(TAG, "button init succeed, io=%d plr=%d",  s_button[i].io_num, s_button[i].polarity);
     }
 #endif
     s_board_button_is_init = true;
 #elif defined(CONFIG_BOARD_BUTTON_INIT)
     ESP_LOGW(TAG, "button Init, but no button defined");
 #endif
     return ESP_OK;
 }
 
 static esp_err_t board_button_deinit()
 {
 #if defined(CONFIG_BOARD_BUTTON_INIT) && (BOARD_BUTTON_NUM > 0)
     BOARD_CHECK(s_board_button_is_init, "adc not inited", ESP_ERR_INVALID_STATE);
     s_board_button_is_init = false;
     ESP_LOGI(TAG, "button deinit succeed");
 #endif
     return ESP_OK;
 }
 
 ATTR_WEAK esp_err_t iot_board_init(void)
 {
     ESP_LOGD(TAG, "%s", __func__);
     BOARD_CHECK(!s_board_is_init, "board has inited", ESP_ERR_INVALID_STATE);
     ESP_LOGI(TAG, "Board Init ...");
 #ifdef BOARD_CALLBACKS
     s_board_callbacks.pre_init_cb = BOARD_CALLBACKS.pre_init_cb;
     s_board_callbacks.pre_deinit_cb = BOARD_CALLBACKS.pre_deinit_cb;
     s_board_callbacks.post_init_cb = BOARD_CALLBACKS.post_init_cb;
     s_board_callbacks.post_deinit_cb = BOARD_CALLBACKS.post_deinit_cb;
     s_board_callbacks.get_handle_cb = BOARD_CALLBACKS.get_handle_cb;
     ESP_LOGI(TAG, "register board specified callbacks");
 #endif
     esp_err_t ret = ESP_OK;
     if (s_board_callbacks.pre_init_cb) {
         ret = s_board_callbacks.pre_init_cb();
         BOARD_CHECK(ret == ESP_OK, "board specific pre_init failed", ret);
     }
 
     ret = board_gpio_init();
     BOARD_CHECK(ret == ESP_OK, "gpio init failed", ret);
 
     ret = board_i2c_bus_init();
     BOARD_CHECK(ret == ESP_OK, "i2c init failed", ret);
 
     ret = board_spi_bus_init();
     BOARD_CHECK(ret == ESP_OK, "spi init failed", ret);
 
 #if BOARD_IO_BASE_XL9535_2
     i2c_xl9535_config_t xl9535_config ={
         .dev_addr = XL9535_I2C_ADDR,
         .clk_speed = 400000,
     };
     for (size_t i = 0; i < XL9535_MAX_I2C_DEVICE; i++)
     {
         xl9535_config.dev_addr = XL9535_I2C_ADDR+i;
         if(ESP_OK != xl9535_init(iot_board_get_handle(BOARD_I2C0_ID), &xl9535_config, &i2c_dev_xl9535[i])){
             BOARD_CHECK(ret == ESP_OK, "xl9535[%d] init failed", i);
         }
     }
 #endif
 
     ret = board_led_init();
     BOARD_CHECK(ret == ESP_OK, "led init failed", ret);
 
     ret = board_button_init();
     BOARD_CHECK(ret == ESP_OK, "button init failed", ret);
 
 
     if (s_board_callbacks.post_init_cb) {
         ret = s_board_callbacks.post_init_cb();
         BOARD_CHECK(ret == ESP_OK, "board specific post_init failed", ret);
     }
 
     s_board_is_init = true;
     ESP_LOGI(TAG, "Board Info: \n\n%s", iot_board_get_info());
     ESP_LOGI(TAG, "Board Init Done !");
     return ESP_OK;
 }
 
 ATTR_WEAK esp_err_t iot_board_deinit(void)
 {
     ESP_LOGD(TAG, "%s", __func__);
     BOARD_CHECK(s_board_is_init, "board not inited", ESP_ERR_INVALID_STATE);
 
     esp_err_t ret = ESP_OK;
     if (s_board_callbacks.pre_deinit_cb) {
         ret = s_board_callbacks.pre_deinit_cb();
         BOARD_CHECK(ret == ESP_OK, "board specific pre_deinit failed", ret);
     }
 
     ret = board_gpio_deinit();
     BOARD_CHECK(ret == ESP_OK, "gpio de-init failed", ret);
 
     ret = board_led_deinit();
     BOARD_CHECK(ret == ESP_OK, "led deinit failed", ret);
 
     ret = board_button_deinit();
     BOARD_CHECK(ret == ESP_OK, "button deinit failed", ret);
 
     ret = board_i2c_bus_deinit();
     BOARD_CHECK(ret == ESP_OK, "i2c deinit failed", ret);
 
     ret = board_spi_bus_deinit();
     BOARD_CHECK(ret == ESP_OK, "spi deinit failed", ret);
 
     if (s_board_callbacks.post_deinit_cb) {
         ret = s_board_callbacks.post_deinit_cb();
         BOARD_CHECK(ret == ESP_OK, "board specific post_deinit failed", ret);
     }
 
     s_board_is_init = false;
     ESP_LOGI(TAG, "Board Deinit Done ...");
     return ESP_OK;
 }
 
 ATTR_WEAK bool iot_board_is_init(void)
 {
     ESP_LOGD(TAG, "%s", __func__);
     return s_board_is_init;
 }
 
 ATTR_WEAK board_res_handle_t iot_board_get_handle(int id)
 {
     ESP_LOGD(TAG, "%s", __func__);
     board_res_handle_t handle = NULL;
     switch (id) {
     case BOARD_I2C0_ID:
         handle = (board_res_handle_t)s_i2c0_bus_handle;
         break;
     // case BOARD_SPI2_ID:
     //     handle = (board_res_handle_t)s_spi2_bus_handle;
     //     break;
     default:
         if (s_board_callbacks.get_handle_cb) {
             handle = s_board_callbacks.get_handle_cb(id);
         }
         break;
     }
     return handle;
 }
 
 ATTR_WEAK char* iot_board_get_info()
 {
     ESP_LOGD(TAG, "%s", __func__);
     return BOARD_INFO;
 }
 
 ATTR_WEAK esp_err_t iot_board_led_set_state(int gpio_num, bool if_on)
 {
     ESP_LOGD(TAG, "%s io%d=%d", __func__, gpio_num, if_on);
     BOARD_CHECK(s_board_led_is_init, "led control gpio not inited", ESP_FAIL);
 #if defined(CONFIG_BOARD_LED_INIT) && (BOARD_LED_NUM > 0)
     int i = 0;
     for (i = 0; i < BOARD_LED_NUM; i++) {
         if (s_led[i].io_num == gpio_num) {
             break;
         }
     }
 
     if (i >= BOARD_LED_NUM) {
         ESP_LOGE(TAG, "GPIO %d is not a valid LED", gpio_num);
         return ESP_FAIL;
     }
 
     if (s_led[i].type == LED_TYPE_GPIO) {
         if (!s_led[i].polarity) { /*check led polarity*/
             if_on = !if_on;
         }
         gpio_set_level(gpio_num, if_on);
     } 
 #if BOARD_IO_BASE_XL9535_2
     else if(s_led[i].type == LED_TYPE_I2C_EXIO) {
         if (!s_led[i].polarity) { /*check led polarity*/
             if_on = !if_on;
         }
         if(if_on){
             // 第一路IO扩展芯片
             if(gpio_num >= BOARD_IO_BASE_XL9535 && gpio_num < BOARD_IO_BASE_XL9535_2){
                 xl9535_digital_write(i2c_dev_xl9535[0], gpio_num-BOARD_IO_BASE_XL9535, 1);
             }
             // 第二路IO扩展芯片
             else {
                 xl9535_digital_write(i2c_dev_xl9535[1], gpio_num-BOARD_IO_BASE_XL9535_2, 1);
             }
         }else{
             // xl9535_digital_write(i2c_dev_xl9535, gpio_num-BOARD_IO_BASE_XL9535, 0);
             // 第一路IO扩展芯片
             if(gpio_num >= BOARD_IO_BASE_XL9535 && gpio_num < BOARD_IO_BASE_XL9535_2){
                 xl9535_digital_write(i2c_dev_xl9535[0], gpio_num-BOARD_IO_BASE_XL9535, 0);
             }
             // 第二路IO扩展芯片
             else {
                 xl9535_digital_write(i2c_dev_xl9535[1], gpio_num-BOARD_IO_BASE_XL9535_2, 0);
             }
         }
     }
 #endif
 #ifdef LED_TYPE_RGB
     else if (s_led[i].type == LED_TYPE_RGB) {
         if (if_on) {
             /* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
             s_led[i].p_strip->set_pixel(s_led[i].p_strip, 0, 16, 16, 16);
             /* Refresh the strip to send data */
             s_led[i].p_strip->refresh(s_led[i].p_strip, 100);
         } else {
             /* Set all LED off to clear all pixels */
             s_led[i].p_strip->clear(s_led[i].p_strip, 50);
         }
     }
 #endif
     s_led[i].state = if_on;
 #endif
     return ESP_OK;
 }
 
 ATTR_WEAK esp_err_t iot_board_led_all_set_state(bool if_on)
 {
     ESP_LOGD(TAG, "%s %d", __func__, if_on);
     BOARD_CHECK(s_board_led_is_init, "led control gpio not inited", ESP_FAIL);
 #if defined(CONFIG_BOARD_LED_INIT) && (BOARD_LED_NUM > 0)
     for (size_t i = 0; i < BOARD_LED_NUM; i++) {
         iot_board_led_set_state(s_led[i].io_num, if_on);
     }
 #endif
     return ESP_OK;
 }
 
 ATTR_WEAK esp_err_t iot_board_led_toggle_state(int gpio_num)
 {
     ESP_LOGD(TAG, "%s io%d", __func__, gpio_num);
     BOARD_CHECK(s_board_led_is_init, "led control gpio not inited", ESP_FAIL);
 #if defined(CONFIG_BOARD_LED_INIT) && (BOARD_LED_NUM > 0)
     size_t i = 0;
     for (; i < BOARD_LED_NUM; i++) {
         if (s_led[i].io_num == gpio_num) {
             break;
         }
     }
     iot_board_led_set_state(gpio_num, !s_led[i].state);
 #endif
     return ESP_OK;
 }
 
 
 ATTR_WEAK uint8_t iot_board_led_get_state(int gpio_num)
 {
     ESP_LOGD(TAG, "%s io%d", __func__, gpio_num);
     BOARD_CHECK(s_board_led_is_init, "led control gpio not inited", ESP_FAIL);
 #if defined(CONFIG_BOARD_LED_INIT) && (BOARD_LED_NUM > 0)
     int i = 0;
     for (i = 0; i < BOARD_LED_NUM; i++) {
         if (s_led[i].io_num == gpio_num) {
             break;
         }
     }
 
     if (i >= BOARD_LED_NUM) {
         ESP_LOGE(TAG, "GPIO %d is not a valid LED", gpio_num);
         return ESP_FAIL;
     }
 
     if (!s_led[i].polarity) { /*check led polarity*/
         return !s_led[i].state;
     }
     return s_led[i].state;
 #endif
     return ESP_OK;
 }
 
 ATTR_WEAK uint8_t iot_board_button_get_state(int gpio_num)
 {
     ESP_LOGD(TAG, "%s io%d", __func__, gpio_num);
     BOARD_CHECK(s_board_button_is_init, "button not inited", ESP_FAIL);
 #if defined(CONFIG_BOARD_BUTTON_INIT) && (BOARD_BUTTON_NUM > 0)
     int i = 0;
     for (i = 0; i < BOARD_BUTTON_NUM; i++) {
         if (s_button[i].io_num == gpio_num) {
             break;
         }
     }
 
     if (i >= BOARD_BUTTON_NUM) {
         ESP_LOGE(TAG, "GPIO %d is not a valid button", gpio_num);
         return ESP_FAIL;
     }
 
     if (!s_button[i].polarity) { /*check button polarity*/
         return !gpio_get_level(gpio_num);
     }
     return gpio_get_level(gpio_num);
 #endif
     return ESP_OK;
 }
 
 ATTR_WEAK esp_err_t iot_board_button_register_cb(int gpio_num, button_event_t event, button_cb_t cb)
 {
     ESP_LOGD(TAG, "%s", __func__);
     BOARD_CHECK(s_board_button_is_init, "button not inited", ESP_FAIL);
     button_handle_t btn_handle = board_button_io_to_handle(gpio_num);
     BOARD_CHECK(btn_handle != NULL && event < BUTTON_NONE_PRESS && cb != NULL, "invalid args", ESP_ERR_INVALID_ARG);
     return iot_button_register_cb(btn_handle, event, cb, NULL);
 }
 
 ATTR_WEAK esp_err_t iot_board_button_unregister_cb(int gpio_num, button_event_t event)
 {
     ESP_LOGD(TAG, "%s", __func__);
     BOARD_CHECK(s_board_button_is_init, "button not inited", ESP_FAIL);
     button_handle_t btn_handle = board_button_io_to_handle(gpio_num);
     BOARD_CHECK(btn_handle != NULL && event < BUTTON_NONE_PRESS, "invalid args", ESP_ERR_INVALID_ARG);
     return iot_button_unregister_cb(btn_handle, event);
 }
 